
#ifdef __linux__

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

#include "../vcd-ping.h"
#include "../vcd-conf/vcd-conf.h"
#include "../vcd-log.h"

typedef struct st_vcd_ping_linux{
    VCD_PING_CALLBACK func;
    GThread *thd;
    gpointer priv;
    gboolean bStop;
}VcdPingLinuxCtx;

static VcdPingLinuxCtx* g_ping_ctx;

static void vcd_ping_linux_stop();

// 4 packets transmitted, 4 received, 0% packet loss, time 3072ms
static gboolean is_packet_loss_str(const char* data){
    if(!data) return FALSE;

    const char* sub = "packet loss";
    if(strstr(data,sub)){
        return TRUE;
    }
    return FALSE;
}
// rtt min/avg/max/mdev = 0.578/0.598/0.625/0.019 ms
static gboolean is_avg_delay_str(const char* data){
    if(!data) return FALSE;

    const char* sub = "rtt min/avg/max/mdev = ";
    if(strstr(data,sub)){
        return TRUE;
    }
    return FALSE;
}

static int find_packet_loss(const char* data){

    if(!is_packet_loss_str(data)){
        goto fail;
    }

    const char* sub = "received, ";
    char* pStar = strstr(data,sub);
    if(!pStar) goto fail;

    pStar += strlen(sub);

    char* pEnd  = strstr(pStar,"%");
    if(!pEnd)  goto fail;

    char tmp[10] = {0};
    strncpy(tmp,pStar,pEnd-pStar);

    int loss = atoi(tmp);
    return loss;
fail:
    return -1;
}
// rtt min/avg/max/mdev = 0.578/0.598/0.625/0.019 ms
static float find_avg_delay(const char* data){
    if(!is_avg_delay_str(data)){
        goto fail;
    }

    const char* sub = "rtt min/avg/max/mdev = ";
    char* pStar = strstr(data,sub);
    if(!pStar) goto fail;

    // 0.578/0.598/0.625/0.019 ms
    pStar += strlen(sub);

    pStar = strstr(pStar,"/");
    if(!pStar) goto fail;

    // 0.598/0.625/0.019 ms
    pStar = pStar + 1;

    char* pEnd  = strstr(pStar,"/");
    if(!pEnd)  goto fail;

    char tmp[10] = {0};
    strncpy(tmp,pStar,pEnd-pStar);

    float delay = atof(tmp);

    return delay;
fail:
    return -1;
}

static gpointer GThreadFunc_ping(gpointer arg){

    while(g_ping_ctx && !g_ping_ctx->bStop){

        char* host = g_vcd_conf->ip;
        if(!host) goto fail;

        char cmd[100] = {0};
        snprintf(cmd, sizeof(cmd), "ping -c 4 -W 1 %s", host);

        FILE* pf = popen(cmd, "r");
        if(!pf) goto fail;

        const int len = 1024;
        char data[1024] = {0};

        int loss = -1;
        float delay = -1;

        while(fgets((char*)data, len, pf)){
            //M_DEBUG("fgets:%s",data);
            if(is_packet_loss_str(data))
                loss = find_packet_loss(data);
            if(is_avg_delay_str(data))
                delay = find_avg_delay(data);
        }

        if(loss != -1 && delay != -1 && g_ping_ctx && g_ping_ctx->func){
            StVcdPingVar ctx = {0};
            ctx.delay = delay;
            ctx.pkt_loss_rate = loss;
            ctx.priv = g_ping_ctx->priv;
            g_ping_ctx->func(&ctx);
        }
        pclose(pf);
        continue;
fail:
        sleep(1);
        continue;
    }
    return FALSE;
}

static gboolean vcd_ping_linux_start(VCD_PING_CALLBACK func,gpointer priv){

    if (g_ping_ctx)
        vcd_ping_linux_stop();
    M_DEBUG("vcd_ping_linux_start");
    g_ping_ctx = g_malloc0(sizeof(VcdPingLinuxCtx));

    g_ping_ctx->priv = priv;
    g_ping_ctx->func = func;

    g_ping_ctx->thd = g_thread_new("vcd_ping_linux", (GThreadFunc)GThreadFunc_ping, NULL);

    return TRUE;
}


static void vcd_ping_linux_stop(){
    g_return_if_fail(g_ping_ctx != 0);
    M_DEBUG("vcd_ping_linux_stop");

    g_ping_ctx->bStop = TRUE;

    system("killall ping");

    g_thread_join(g_ping_ctx->thd);

    g_free(g_ping_ctx);
    g_ping_ctx = 0;
}

struct StVcdPingCtx g_vcd_ping_linux = {
    .start        = vcd_ping_linux_start,
    .stop         = vcd_ping_linux_stop
};

#endif // __linux__
